package com.hfy.music.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.nacos.common.codec.Base64;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hfy.feign.IUserClient;
import com.hfy.model.dto.together.CreateRoomDto;
import com.hfy.model.dto.together.GenerateInviteDto;
import com.hfy.model.dto.together.InviteDto;
import com.hfy.model.po.user.UserCommentPo;
import com.hfy.model.po.together.TogetherInvitePo;
import com.hfy.model.vo.MusicVo;
import com.hfy.model.vo.together.InviteInfoVo;
import com.hfy.model.vo.user.SimpleUserVo;
import com.hfy.music.mapper.DetailMusicMapper;
import com.hfy.music.mapper.ListenMusicTogetherMapper;
import com.hfy.music.mapper.MusicUserMapper;
import com.hfy.music.mapper.SheetMusicMapper;
import com.hfy.music.service.ListenMusicTogetherService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import result.Result;
import utils.MusicConverter;

import java.time.LocalDateTime;
import java.util.List;

import static constants.OtherConstants.FONT_URL_PREFIX;
import static constants.OtherConstants.TOGETHER_INVITE_MIDFIX;
import static constants.RedisConstants.*;

/**
 * @ author 韩
 * time: 2024/10/7 12:19
 */
@Service
public class ListenMusicTogetherServiceImpl extends ServiceImpl<ListenMusicTogetherMapper, TogetherInvitePo> implements ListenMusicTogetherService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private IUserClient userClient;

    @Autowired
    private DetailMusicMapper detailMusicMapper;

    @Autowired
    private MusicUserMapper musicUserMapper;

    @Autowired
    private SheetMusicMapper sheetMusicMapper;

    @Override
    @Transactional
    public Result acceptInvite(InviteDto dto) {

        // 0. 准备参数
        String roomId = dto.getRoomId();
        // 1.1 redis 做校参
        if (StringUtils.isBlank(roomId)) return Result.error("房间号万万不能为空！");
        // 1.2 再去查一下redis里还有没有该房间号
        String roomKey = TOGETHER_ROOM_KEY + roomId;
        Boolean hasRoom = redisTemplate.hasKey(roomKey);
        if (Boolean.FALSE.equals(hasRoom)) return Result.error("该房间已经不存在");

        TogetherInvitePo invitePo = getById(roomId);

        if (invitePo == null) return Result.error("找不到该房间");
        // 房间状态
        Integer status = invitePo.getStatus();

        if (status == 2) return Result.error("该房间已经满员");
        if (status == 3) return Result.error("该房间已经关闭");

        // 被邀请人的账号
        String inviteeAccount = dto.getInviteeAccount();

        LocalDateTime expireTime = invitePo.getExpireTime();
        LocalDateTime now = LocalDateTime.now();



        // 2. 再用mysql一些业务逻辑判断
        // 校验一下该邀请是否过期
        if (now.isAfter(expireTime)) {
            // 如果现在在过期时间之后就证明过期了
            return Result.error("该邀请已经过期");
        }

//        Boolean isMember = redisTemplate.opsForHash().hasKey(roomKey, inviteeAccount);
//        if (isMember) return Result.error("已经加入了该房间");
//        Integer size = redisTemplate.opsForHash().keys(roomKey).size();
//        if (size <= 0) return Result.error("房主已退出");
//        if (size >= 2) return Result.error("房间已经满了");

        if (inviteeAccount.equals(invitePo.getInviterAccount())) {
            log.error("不能邀请自己，禁止自娱自乐");
            return Result.error("不能邀请自己，禁止自娱自乐");
        }

        // 3. 执行业务操作

        // 删除痕迹，获取原来的房间号，然后直接删除房间
        String originalRoomId = redisTemplate.opsForValue().get(TOGETHER_USER_KEY + inviteeAccount);
        redisTemplate.opsForHash().delete(TOGETHER_ROOM_KEY + originalRoomId);

        redisTemplate.opsForValue().set(TOGETHER_USER_KEY + inviteeAccount, roomId);

        // 这里对邀请改成失效状态
        LambdaUpdateWrapper<TogetherInvitePo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(TogetherInvitePo::getStatus, 3).eq(TogetherInvitePo::getRoomId, TOGETHER_ROOM_KEY + roomId);
        update(updateWrapper);


        redisTemplate.opsForHash().put(roomKey, TOGETHER_HASH_VISITOR_KEY, inviteeAccount);
        updateById(invitePo.setStatus(2));
        return Result.ok();
    }

    @Override
    public Result generateInviteInfo(GenerateInviteDto dto) {

        TogetherInvitePo togetherInvitePo = BeanUtil.copyProperties(dto, TogetherInvitePo.class);
        // 一小时后过期
        togetherInvitePo.setExpireTime(LocalDateTime.now().plusHours(1));
        saveOrUpdate(togetherInvitePo);

        // 生成的邀请链接并进行加密
        byte[] encodedBytes = Base64.encodeBase64(dto.getRoomId().getBytes());
        String url = FONT_URL_PREFIX + TOGETHER_INVITE_MIDFIX + new String(encodedBytes);

        return Result.ok(url);
    }

    @Override
    public Result createRoom(CreateRoomDto dto) {
        String inviterAccount = dto.getInviterAccount();
        // 一个账号只能拥有一个房间，



        // 1. 做基本校验
        if (inviterAccount == null) return Result.error("账号不能为空");

        // 查询该账号下的邀请的房间的status置为3，
        // 这里关闭房间
        LambdaUpdateWrapper<TogetherInvitePo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(TogetherInvitePo::getStatus, 3).eq(TogetherInvitePo::getInviterAccount, inviterAccount);
        update(updateWrapper);

        // 2. 创建房间号
        String uuid = UUID.randomUUID().toString(true);
        String roomId = TOGETHER_ROOM_KEY + uuid;

        // 查询redis查看这个用户现在在什么房间里，如果没有进入房间那么就新建，如果有就返回
        String nowRoomId = redisTemplate.opsForValue().get(TOGETHER_USER_KEY + inviterAccount);

        // 如果有房间过期逻辑的话，就要去检测一下房间是否过期，如果过期了，就要继续创建房间逻辑
        if (!nowRoomId.equals("0")) {
            return Result.ok(nowRoomId);
        }
        // 3. 保存在redis里


        redisTemplate.opsForValue().set(TOGETHER_USER_KEY + inviterAccount, uuid);


        redisTemplate.opsForHash().put(roomId, TOGETHER_HASH_OWNER_KEY, inviterAccount);
        // 4. 返回给前端
        return Result.ok(uuid);
        // 5. 成功后前端开启websocket

        // https://c6.y.qq.com/base/fcgi-bin/u?__=yFPwYKBbAyHj
        // https://c6.y.qq.com/base/fcgi-bin/u?__=yFPwYKBbAyHj
    }

    @Override
    public Result getInviteInfo(String roomId) {
        // 0. 准备参数
        if (roomId == null) return Result.error("404", "404");

        TogetherInvitePo invitePo = getById(roomId);
        if (invitePo == null) return Result.error("404", "404");

        // 邀请人账号
        String inviterAccount = invitePo.getInviterAccount();
        // 邀请的音乐id
        Integer musicId = invitePo.getMusicId();
        // 1. 校验参数

        // 房间状态
        Integer status = invitePo.getStatus();

        LocalDateTime expireTime = invitePo.getExpireTime();
        LocalDateTime now = LocalDateTime.now();



        // 然后去根据inviterAccount去查 用户数据 和根据musicId查音乐数据
        UserCommentPo userInfo = userClient.getInfoByAccount(Long.valueOf(inviterAccount));

        MusicVo musicVo = detailMusicMapper.queryMusicDetail(musicId);
        // 然后组装vo
        InviteInfoVo vo = new InviteInfoVo();
        vo.setAvatar(userInfo.getAvatar());
        vo.setUsername(userInfo.getUsername());
        vo.setMusician(musicVo.getMusician());
        vo.setCover(musicVo.getCover());
        vo.setMusicName(musicVo.getName());

        if (now.isAfter(expireTime) || status != 1) {
            // 如果现在在过期时间之后就证明过期 邀请过期或者房间满员或房间关闭，都会导致失效
            return Result.ok(vo.setIsUnValid(true));
        }

        return Result.ok(vo);
    }

    @Override
    public Result getOwnerMusicList(String roomId, String sheetId) {

        // 要返回的音乐列表
        List<MusicVo> musicList;
        if (StringUtils.isBlank(sheetId) || sheetId.equals("null")) {
            // 查询
            if (StringUtils.isBlank(roomId)) return Result.error("房间不能为空");
            String ownerAccount = (String) redisTemplate.opsForHash().get(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_OWNER_KEY);
            if (ownerAccount == null) {

                return Result.ok("again" , "redis里的房间消失，你应该重新创建房间");
            }
            musicList = musicUserMapper.getMusic(Long.valueOf(ownerAccount));

        } else {
            // 要根据sheetId去查询音乐列表
            musicList = sheetMusicMapper.getMusicList(sheetId);
        }


        return MusicConverter.getMusicResult(musicList);
    }

    @Override
    public Result getMyIdentity(String roomId, String account) {
        if (StringUtils.isBlank(roomId)) return Result.error("房间不能为空");
        if (StringUtils.isBlank(account)) return Result.error("账号不能为空");
        String ownerAccount = (String) redisTemplate.opsForHash().get(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_OWNER_KEY);
        boolean isOwner = false;
        if (ownerAccount != null) {
            isOwner = ownerAccount.equals(account);
        }
        return Result.ok(isOwner);
    }

    @Override
    public Result getOwnerInfo(String roomId) {
        // 查询
        if (StringUtils.isBlank(roomId)) return Result.error("房间不能为空");
        String ownerAccount = (String) redisTemplate.opsForHash().get(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_OWNER_KEY);
        UserCommentPo userInfo = null;
        if (ownerAccount != null) {
            userInfo = userClient.getInfoByAccount(Long.valueOf(ownerAccount));
        }
        SimpleUserVo simpleUserVo = new SimpleUserVo();
        if (ownerAccount != null) {
            simpleUserVo.setAccount(Long.valueOf(ownerAccount)).setUsername(userInfo.getUsername()).setAvatar(userInfo.getAvatar()).setExist(true);
        }
        return Result.ok(simpleUserVo);
    }
    @Override
    public Result getVisitorInfo(String roomId) {
        // 查询
        if (StringUtils.isBlank(roomId)) return Result.error("房间不能为空");
        String visitorAccount = (String) redisTemplate.opsForHash().get(TOGETHER_ROOM_KEY + roomId, TOGETHER_HASH_VISITOR_KEY);
        UserCommentPo userInfo = null;
        if (visitorAccount != null) {
            userInfo = userClient.getInfoByAccount(Long.valueOf(visitorAccount));
        }
        SimpleUserVo simpleUserVo = new SimpleUserVo();
        if (visitorAccount != null) {
            simpleUserVo.setAccount(Long.valueOf(visitorAccount)).setUsername(userInfo.getUsername()).setAvatar(userInfo.getAvatar()).setExist(true);
        }
        return Result.ok(simpleUserVo);
    }

}

